/**
 * 
 */
package org.statewalker.parser.turtle.tokenizer;

import org.statewalker.tokenizer.CharStream;
import org.statewalker.tokenizer.ITokenizer;
import org.statewalker.tokenizer.StreamToken;
import org.statewalker.tokenizer.CharStream.Marker;

/**
 * <pre>
 * http://www.w3.org/TeamSubmission/turtle/#prefixName
 * [33] prefixName  ::= ( nameStartChar - '_' ) nameChar*
 * [30] nameStartChar   ::= [A-Z] | "_" | [a-z] | [#x00C0-#x00D6] | [#x00D8-#x00F6] | [#x00F8-#x02FF] | [#x0370-#x037D] | [#x037F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
 * [31] nameChar    ::= nameStartChar | '-' | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040]
 * </pre>
 * 
 * @author kotelnikov
 */
public class NameTokenizer implements ITokenizer {

    public static class NameToken extends StreamToken {

        public NameToken(String key, CharStream stream, Marker marker) {
            super(key, stream, marker);
        }

    }

    /**
     * <pre>
     * [30] nameStartChar   ::= [A-Z] | "_" | [a-z] | [#x00C0-#x00D6] | [#x00D8-#x00F6] | [#x00F8-#x02FF] | [#x0370-#x037D] | [#x037F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
     * </pre>
     */
    public static int getNameCharCode(char ch) {
        int code = getNameStartCharCode(ch);
        if (code >= 0)
            return code;
        if (code == '-')
            return ch;
        if (code >= '0' && code <= '9')
            return ch;
        if (ch == 0x00B7)
            return ch;
        if (ch >= 0x0300 && ch <= 0x036F)
            return ch;
        if (ch >= 0x203F && ch <= 0x2040)
            return ch;
        return -1;
    }

    /**
     * <pre>
     * [31] nameChar    ::= nameStartChar | '-' | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040]
     * </pre>
     */
    public static int getNameStartCharCode(char ch) {
        if (ch >= 'A' && ch <= 'Z')
            return ch;
        if (ch == '_')
            return ch;
        if (ch >= 'a' && ch <= 'z')
            return ch;
        if (ch >= 0x00C0 && ch <= 0x00D6)
            return ch;
        if (ch >= 0x00D8 && ch <= 0x00F6)
            return ch;
        if (ch >= 0x00F8 && ch <= 0x02FF)
            return ch;
        if (ch >= 0x0370 && ch <= 0x037D)
            return ch;
        if (ch >= 0x037F && ch <= 0x1FFF)
            return ch;
        if (ch >= 0x200C && ch <= 0x200D)
            return ch;
        if (ch >= 0x2070 && ch <= 0x218F)
            return ch;
        if (ch >= 0x2C00 && ch <= 0x2FEF)
            return ch;
        if (ch >= 0x3001 && ch <= 0xD7FF)
            return ch;
        if (ch >= 0xF900 && ch <= 0xFDCF)
            return ch;
        if (ch >= 0xFDF0 && ch <= 0xFFFD)
            return ch;
        if (ch >= 0x10000 && ch <= 0xEFFFF)
            return ch;
        return -1;
    }

    private String fKey;

    /**
     * 
     */
    public NameTokenizer(String key) {
        fKey = key;
    }

    protected boolean isExcludedCharacter(char ch) {
        return false;
    }

    /**
     * <pre>
     * [33] prefixName  ::= ( nameStartChar - '_' ) nameChar*
     * </pre>
     * 
     * @see org.statewalker.tokenizer.ITokenizer#read(org.statewalker.tokenizer.CharStream)
     */
    public StreamToken read(CharStream stream) {
        char ch = stream.getChar();
        int code = getNameStartCharCode(ch);
        if (code < 0 || isExcludedCharacter(ch))
            return null;
        NameToken result = null;
        Marker marker = stream.markPosition();
        try {
            while (stream.incPos()) {
                ch = stream.getChar();
                code = getNameCharCode(ch);
                if (code < 0)
                    break;
            }
            result = new NameToken(fKey, stream, marker);
            return result;
        } finally {
            marker.close(result == null);
        }
    }

}
